home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 June / MacFormat 25.iso / Shareware City / Developers / OutOfPhase1.1 Source / OutOfPhase Folder / Level 0 Macintosh 01Jan95 / SerialPort.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-21  |  17.1 KB  |  577 lines  |  [TEXT/KAHL]

  1. /* SerialPort.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    System Dependency Library for Building Portable Software               */
  5. /*    Macintosh Version                                                      */
  6. /*    Written by Thomas R. Lawrence, 1993 - 1994.                            */
  7. /*                                                                           */
  8. /*    This file is Public Domain; it may be used for any purpose whatsoever  */
  9. /*    without restriction.                                                   */
  10. /*                                                                           */
  11. /*    This package is distributed in the hope that it will be useful,        */
  12. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  13. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   */
  14. /*                                                                           */
  15. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  16. /*                                                                           */
  17. /*****************************************************************************/
  18.  
  19. #include "MiscInfo.h"
  20. #include "Audit.h"
  21. #include "Debug.h"
  22. #include "Definitions.h"
  23.  
  24. #ifdef THINK_C
  25.     #pragma options(pack_enums)
  26. #endif
  27. #include <Serial.h>
  28. #include <Devices.h>
  29. #include <Events.h>
  30. #ifdef THINK_C
  31.     #pragma options(!pack_enums)
  32. #endif
  33.  
  34. #include "SerialPort.h"
  35. #include "Memory.h"
  36. #include "EventLoop.h"
  37. #include "Array.h"
  38.  
  39.  
  40. #define DC1 (17) /* XOn character */
  41. #define DC3 (19) /* XOff character */
  42.  
  43. /* this value is the length of time we'll wait for a write cell to drain.  if */
  44. /* it doesn't drain in this much time, we return a timeout error. */
  45. /* time units are in 1/60ths of a second */
  46. #define IOTIMEOUT (6*60)  /* 6 seconds to write a byte to the output buffer */
  47.  
  48. /* this defines the size of the input buffer */
  49. #define INPUTBUFFERSIZE (16384)
  50.  
  51. /* this defines how many bytes a single asynchronous write operation can handle. */
  52. #define WRITECELLSIZE (384)
  53.  
  54. /* this defines how many write cells are allocated */
  55. #define NUMWRITECELLS (16)
  56.  
  57. /* this structure is one write cell buffer, and can be used for one asynchronous */
  58. /* write operation */
  59. typedef struct
  60.     {
  61.         ParamBlockRec            MyPB;
  62.         char                            Buffer[WRITECELLSIZE];
  63.         MyBoolean                    InUseFlag;
  64.         long                            NumBytes;
  65.     } WriteCell;
  66.  
  67.  
  68. struct SerialPortRec
  69.     {
  70.         short                        InputPortRefNum;
  71.         short                        OutputPortRefNum;
  72.         MyBoolean                GracePeriodInEffect;
  73.         char                        InputBuffer[INPUTBUFFERSIZE];
  74.         WriteCell                WriteCellArray[NUMWRITECELLS];
  75.     };
  76.  
  77.  
  78. struct SerialRefRec
  79.     {
  80.         long                        PortIndex;
  81.     };
  82.  
  83.  
  84. EXECUTE(static MyBoolean                Initialized = False;)
  85.  
  86. EXECUTE(static long                            RefCount = 0;)
  87.  
  88. EXECUTE(static ArrayRec*                ListOfRefs;)
  89.  
  90.  
  91. /* prototype for callback routine */
  92. #if __powerc
  93. static pascal void    Callback(WriteCell* param);
  94. #else
  95. static pascal void    Callback(void);
  96. #endif
  97.  
  98.  
  99. /* initialize serial port subsystem.  the user calls this.  this is not called */
  100. /* from the normal Level 0 initialization since this module is optional. */
  101. MyBoolean                        InitializeSerialPorts(void)
  102.     {
  103.         ERROR(Initialized,PRERR(ForceAbort,"InitializeSerialPorts:  already initialized"));
  104.         EXECUTE(RefCount = 0;)
  105. #if DEBUG
  106.         ListOfRefs = NewArray();
  107.         if (ListOfRefs == NIL)
  108.             {
  109.                 return False;
  110.             }
  111. #endif
  112.         EXECUTE(Initialized = True;)
  113.         return True;
  114.     }
  115.  
  116.  
  117. /* shut down serial ports */
  118. void                                ShutdownSerialPorts(void)
  119.     {
  120.         ERROR(!Initialized,PRERR(ForceAbort,"ShutdownSerialPorts:  not initialized"));
  121.         ERROR(RefCount != 0,PRERR(AllowResume,
  122.             "ShutdownSerialPorts:  some connections still open"));
  123. #if DEBUG
  124.         ERROR(ArrayGetLength(ListOfRefs) != 0,PRERR(AllowResume,
  125.             "ShutdownSerialPorts:  some references have not been disposed"));
  126.         DisposeArray(ListOfRefs);
  127. #endif
  128.         EXECUTE(Initialized = False;)
  129.     }
  130.  
  131.  
  132. /* request a port.  if the port can not be allocated, it will return NIL. */
  133. SerialPortRec*            RequestSerialPort(long BitsRate, SerialRefRec* PortIdentifier,
  134.                                             ParityTypes Parity, HandShakeTypes HandShake,
  135.                                             DataBitTypes NumDataBits, StopBitTypes NumStopBits)
  136.     {
  137.         SerialPortRec*        SerialPort;
  138.         long                            Scan;
  139.         OSErr                            Error;
  140.         long                            Config;
  141.         short                            BaudTemp;
  142.         SerShk                        Shaker;
  143.  
  144.         ERROR(!Initialized,PRERR(ForceAbort,"RequestSerialPort:  not initialized"));
  145.         /* create the memory block.  it must never relocate after this. */
  146.         ERROR(ArrayFindElement(ListOfRefs,PortIdentifier) == -1,PRERR(ForceAbort,
  147.             "RequestSerialPort:  invalid reference"));
  148.         CheckPtrExistence(PortIdentifier);
  149.         SerialPort = (SerialPortRec*)AllocPtrCanFail(sizeof(SerialPortRec),"SerialPortRec");
  150.         if (SerialPort == NIL)
  151.             {
  152.              FailurePoint1:
  153.                 return NIL;
  154.             }
  155.         /* open the serial port */
  156.         switch (PortIdentifier->PortIndex)
  157.             {
  158.                 default: /* bad serial port ID number */
  159.                     EXECUTE(PRERR(ForceAbort,"RequestSerialPort:  bad serial port ID number"));
  160.                     break;
  161.                 case 0: /* modem port */
  162.                     Error = OpenDriver("\p.AOut",&(SerialPort->OutputPortRefNum));
  163.                     if (Error != noErr)
  164.                         {
  165.                             ReleasePtr((char*)SerialPort);
  166.                             goto FailurePoint1;
  167.                         }
  168.                     Error = OpenDriver("\p.AIn",&(SerialPort->InputPortRefNum));
  169.                     if (Error != noErr)
  170.                         {
  171.                             CloseDriver(SerialPort->OutputPortRefNum);
  172.                             ReleasePtr((char*)SerialPort);
  173.                             goto FailurePoint1;
  174.                         }
  175.                     break;
  176.                 case 1: /* printer port */
  177.                     Error = OpenDriver("\p.BOut",&(SerialPort->OutputPortRefNum));
  178.                     if (Error != noErr)
  179.                         {
  180.                             ReleasePtr((char*)SerialPort);
  181.                             goto FailurePoint1;
  182.                         }
  183.                     Error = OpenDriver("\p.BIn",&(SerialPort->InputPortRefNum));
  184.                     if (Error != noErr)
  185.                         {
  186.                             CloseDriver(SerialPort->OutputPortRefNum);
  187.                             ReleasePtr((char*)SerialPort);
  188.                             goto FailurePoint1;
  189.                         }
  190.                     break;
  191.             }
  192.         /* clear the in-use fields of the write cells */
  193.         for (Scan = 0; Scan < NUMWRITECELLS; Scan += 1)
  194.             {
  195.                 SerialPort->WriteCellArray[Scan].InUseFlag = False;
  196.             }
  197.         /* initialize the various important parameters */
  198.         Config = 0;
  199.         switch (Parity)
  200.             {
  201.                 case eParityNone:
  202.                     Config |= noParity;
  203.                     break;
  204.                 case eParityEven:
  205.                     Config |= evenParity;
  206.                     break;
  207.                 case eParityOdd:
  208.                     Config |= oddParity;
  209.                     break;
  210.                 default:
  211.                     EXECUTE(PRERR(ForceAbort,"Unsupported parity"));
  212.                     break;
  213.             }
  214.         switch (NumDataBits)
  215.             {
  216.                 case e8DataBits:
  217.                     Config |= data8;
  218.                     break;
  219.                 case e7DataBits:
  220.                     Config |= data7;
  221.                     break;
  222.                 case e6DataBits:
  223.                     Config |= data6;
  224.                     break;
  225.                 case e5DataBits:
  226.                     Config |= data5;
  227.                     break;
  228.                 default:
  229.                     EXECUTE(PRERR(ForceAbort,"Unsupported number of data bits"));
  230.                     break;
  231.             }
  232.         switch (NumStopBits)
  233.             {
  234.                 case eOneStopBit:
  235.                     Config |= stop10;
  236.                     break;
  237.                 case eOneAndAHalfStopBits:
  238.                     Config |= stop15;
  239.                     break;
  240.                 case eTwoStopBits:
  241.                     Config |= stop20;
  242.                     break;
  243.                 default:
  244.                     EXECUTE(PRERR(ForceAbort,"Unsupported number of stop bits"));
  245.                     break;
  246.             }
  247.         SerReset(SerialPort->InputPortRefNum,Config);
  248.         SerReset(SerialPort->OutputPortRefNum,Config);
  249.         /* set the baud rate */
  250.         BaudTemp = GetClosestAvailableBaudRate(BitsRate,PortIdentifier);
  251.         Control(SerialPort->OutputPortRefNum,13,&BaudTemp);
  252.         BaudTemp = GetClosestAvailableBaudRate(BitsRate,PortIdentifier);
  253.         Control(SerialPort->InputPortRefNum,13,&BaudTemp);
  254.         /* setting the buffer */
  255.         SerSetBuf(SerialPort->InputPortRefNum,&(SerialPort->InputBuffer[0]),INPUTBUFFERSIZE);
  256.         /* setting handshake parameters */
  257.         Shaker.xOn = DC1;
  258.         Shaker.xOff = DC3;
  259.         Shaker.fXOn = (HandShake == eHandShakeXonXoff);
  260.         Shaker.fCTS = (HandShake == eHandShakeDtrCts) || (HandShake == eHandShakeCtsOnly);
  261.         Shaker.errs = 0;
  262.         Shaker.evts = 0;
  263.         Shaker.fInX = (HandShake == eHandShakeXonXoff);
  264.         Shaker.fDTR = (HandShake == eHandShakeDtrCts) || (HandShake == eHandShakeDtrOnly);
  265.         SerHShake(SerialPort->InputPortRefNum,&Shaker);
  266.         SerHShake(SerialPort->OutputPortRefNum,&Shaker);
  267.         /* connection now established */
  268.         SerialPort->GracePeriodInEffect = False;
  269.         EXECUTE(RefCount += 1;)
  270.         return SerialPort;
  271.     }
  272.  
  273.  
  274. /* close a port */
  275. void                                CloseSerialPort(SerialPortRec* SerialPort)
  276.     {
  277.         ERROR(!Initialized,PRERR(ForceAbort,"CloseSerialPort:  not initialized"));
  278.         CheckPtrExistence(SerialPort);
  279.         /* make sure no callbacks are waiting to be called */
  280.         KillIO(SerialPort->OutputPortRefNum);
  281.         KillIO(SerialPort->InputPortRefNum);
  282.         /* dump the drivers */
  283.         CloseDriver(SerialPort->InputPortRefNum);
  284.         CloseDriver(SerialPort->OutputPortRefNum);
  285.         /* release the memory */
  286.         ReleasePtr((char*)SerialPort);
  287.         EXECUTE(RefCount -= 1;)
  288.     }
  289.  
  290.  
  291. /* get how many serial ports there are on the system */
  292. long                                GetNumSerialPorts(void)
  293.     {
  294.         ERROR(!Initialized,PRERR(ForceAbort,"GetNumSerialPorts:  not initialized"));
  295.         return 2;
  296.     }
  297.  
  298.  
  299. /* get the ID of a serial port from the list */
  300. SerialRefRec*                GetIndexedSerialPort(long Index)
  301.     {
  302.         SerialRefRec*            Ref;
  303.  
  304.         ERROR(!Initialized,PRERR(ForceAbort,"GetIndexedSerialPort:  not initialized"));
  305.         ERROR((Index < 0) || (Index >= GetNumSerialPorts()),PRERR(ForceAbort,
  306.             "GetIndexedSerialPort:  port index is out of range"));
  307.         Ref = (SerialRefRec*)AllocPtrCanFail(sizeof(SerialRefRec),"SerialRefRec");
  308.         if (Ref == NIL)
  309.             {
  310.              FailurePoint1:
  311.                 return NIL;
  312.             }
  313.         Ref->PortIndex = Index;
  314.         EXECUTE(if (!ArrayAppendElement(ListOfRefs,Ref)) {ReleasePtr((char*)Ref); Ref = NIL;});
  315.         return Ref;
  316.     }
  317.  
  318.  
  319. /* get the name associated with a serial port identifier */
  320. char*                                GetSerialPortName(SerialRefRec* TheIdentifier)
  321.     {
  322.         char*                            Name;
  323.  
  324.         ERROR(!Initialized,PRERR(ForceAbort,"GetSerialPortName:  not initialized"));
  325.         ERROR(ArrayFindElement(ListOfRefs,TheIdentifier) == -1,PRERR(ForceAbort,
  326.             "GetSerialPortName:  invalid reference"));
  327.         CheckPtrExistence(TheIdentifier);
  328.         switch (TheIdentifier->PortIndex)
  329.             {
  330.                 case 0:  /* modem port */
  331.                     Name = AllocPtrCanFail(10,"SerialPortName");
  332.                     if (Name != NIL)
  333.                         {
  334.                             CopyData("Modem Port",Name,10);
  335.                         }
  336.                     break;
  337.                 case 1:  /* printer port */
  338.                     Name = AllocPtrCanFail(12,"SerialPortName");
  339.                     if (Name != NIL)
  340.                         {
  341.                             CopyData("Printer Port",Name,12);
  342.                         }
  343.                     break;
  344.                 default:
  345.                     EXECUTE(PRERR(ForceAbort,"GetSerialPortName:  bad port number"));
  346.                     break;
  347.             }
  348.         return Name;
  349.     }
  350.  
  351.  
  352. /* dispose of a serial port reference */
  353. void                                DisposeSerialRef(SerialRefRec* TheIdentifier)
  354.     {
  355.         CheckPtrExistence(TheIdentifier);
  356.         ERROR(ArrayFindElement(ListOfRefs,TheIdentifier) == -1,PRERR(ForceAbort,
  357.             "DisposeSerialRef:  invalid reference"));
  358.         EXECUTE(ArrayDeleteElement(ListOfRefs,ArrayFindElement(ListOfRefs,TheIdentifier)));
  359.         ReleasePtr((char*)TheIdentifier);
  360.     }
  361.  
  362.  
  363. /* find out the closest available baud rate to the one requested.  if the */
  364. /* requested baud rate is supported, it is returned.  if not, then the closest */
  365. /* available rate is returned. */
  366. long                                GetClosestAvailableBaudRate(long RequestedRate,
  367.                                             SerialRefRec* PortIdentifier)
  368.     {
  369.         ERROR(!Initialized,PRERR(ForceAbort,
  370.             "GetClosestAvailableBaudRate:  not initialized"));
  371.         ERROR(ArrayFindElement(ListOfRefs,PortIdentifier) == -1,PRERR(ForceAbort,
  372.             "GetClosestAvailableBaudRate:  invalid reference"));
  373.         ERROR((PortIdentifier->PortIndex < 0) || (PortIdentifier->PortIndex >= 2),
  374.             PRERR(ForceAbort,"GetClosestAvailableBaudRate:  bad port number"));
  375.         CheckPtrExistence(PortIdentifier);
  376.         /* for Macintosh, this doesn't actually depend on the port kind, but on */
  377.         /* some systems it might */
  378.         if (RequestedRate < 300)
  379.             {
  380.                 return 300;
  381.             }
  382.         else if (RequestedRate > 57600)
  383.             {
  384.                 return 57600;
  385.             }
  386.         else
  387.             {
  388.                 return RequestedRate;
  389.             }
  390.     }
  391.  
  392.  
  393. /* find out how much data is waiting to be read */
  394. long                                NumSerialPortBytesWaitingToRead(SerialPortRec* SerialPort)
  395.     {
  396.         long                            NumBytes;
  397.  
  398.         ERROR(!Initialized,PRERR(ForceAbort,
  399.             "NumSerialPortBytesWaitingToRead:  not initialized"));
  400.         CheckPtrExistence(SerialPort);
  401.         SerGetBuf(SerialPort->InputPortRefNum,&NumBytes);
  402.         return NumBytes;
  403.     }
  404.  
  405.  
  406. /* find out how much data is waiting to leave the local buffers */
  407. long                                NumSerialPortBytesWaitingToWrite(SerialPortRec* SerialPort)
  408.     {
  409.         long                            Scan;
  410.         long                            Count;
  411.  
  412.         ERROR(!Initialized,PRERR(ForceAbort,
  413.             "NumSerialPortBytesWaitingToWrite:  not initialized"));
  414.         CheckPtrExistence(SerialPort);
  415.         Count = 0;
  416.         for (Scan = 0; Scan < NUMWRITECELLS; Scan += 1)
  417.             {
  418.                 if (SerialPort->WriteCellArray[Scan].InUseFlag)
  419.                     {
  420.                         /* if the flag gets cleared right before we read it, well, too bad... */
  421.                         Count += SerialPort->WriteCellArray[Scan].NumBytes;
  422.                     }
  423.             }
  424.         return Count;
  425.     }
  426.  
  427.  
  428. /* read some bytes from the port buffer into the specified buffer.  it is an */
  429. /* error to read more bytes than there are waiting. */
  430. void                                ReadSerialPort(SerialPortRec* SerialPort, long NumBytesToRead,
  431.                                             char* Buffer)
  432.     {
  433.         OSErr                            Error;
  434.         long                            OldNumBytes;
  435.  
  436.         ERROR(!Initialized,PRERR(ForceAbort,"ReadSerialPort:  not initialized"));
  437.         ERROR(NumSerialPortBytesWaitingToRead(SerialPort) < NumBytesToRead,
  438.             PRERR(AllowResume,"ReadSerialPort:  reading too many bytes"));
  439.         EXECUTE(OldNumBytes = NumBytesToRead;)
  440.         Error = FSRead(SerialPort->InputPortRefNum,&NumBytesToRead,&(Buffer[0]));
  441.         ERROR(OldNumBytes != NumBytesToRead,PRERR(ForceAbort,
  442.             "ReadSerialPort: [FSRead] read error -- didn't read all the bytes even though they exist"));
  443.     }
  444.  
  445.  
  446. /* submit bytes to be written.  it returns True if successful, or False if */
  447. /* the operation timed out or another error occurred */
  448. MyBoolean                        WriteSerialPort(SerialPortRec* SerialPort, long Length, char* Data)
  449.     {
  450.         long                        StartTime;
  451.         short                        ChosenWriteCell;
  452.         short                        Scan;
  453.  
  454.         ERROR(!Initialized,PRERR(ForceAbort,"WriteSerialPort:  not initialized"));
  455.         CheckPtrExistence(SerialPort);
  456.  
  457.         if (Length == 0)
  458.             {
  459.                 return True;
  460.             }
  461.  
  462.         /* entry point for writing chunks of data */
  463.      ReEntryPoint:
  464.         ChosenWriteCell = -1;
  465.         StartTime = TickCount();
  466.         /* here we loop until we can find a free write cell */
  467.         while (ChosenWriteCell == -1)
  468.             {
  469.                 /* check timeout */
  470.                 if (TickCount() - StartTime > IOTIMEOUT)
  471.                     {
  472.                         /* a timeout occurred because we looped for a while and were unable */
  473.                         /* to write any data. */
  474.                         return False;
  475.                     }
  476.                 /* look for available write cells */
  477.                 for (Scan = 0; (Scan < NUMWRITECELLS) && (ChosenWriteCell == -1); Scan += 1)
  478.                     {
  479.                         if (!SerialPort->WriteCellArray[Scan].InUseFlag)
  480.                             {
  481.                                 ChosenWriteCell = Scan;
  482.                             }
  483.                     }
  484.                 /* relinquish CPU so that user can do something productive while waiting. */
  485.                 if (ChosenWriteCell == -1)
  486.                     {
  487.                         RelinquishCPUCheckCancel();
  488.                     }
  489.             }
  490.  
  491.         /* we found a buffer, so we can set it up for the async. write */
  492.         SerialPort->WriteCellArray[ChosenWriteCell].InUseFlag = True;
  493.         SerialPort->WriteCellArray[ChosenWriteCell].MyPB.ioParam.ioCompletion
  494.             = (ProcPtr)&Callback;
  495.         SerialPort->WriteCellArray[ChosenWriteCell].MyPB.ioParam.ioVRefNum = 0;
  496.         SerialPort->WriteCellArray[ChosenWriteCell].MyPB.ioParam.ioRefNum
  497.             = SerialPort->OutputPortRefNum;
  498.         SerialPort->WriteCellArray[ChosenWriteCell].MyPB.ioParam.ioBuffer
  499.             = SerialPort->WriteCellArray[ChosenWriteCell].Buffer;
  500.         SerialPort->WriteCellArray[ChosenWriteCell].MyPB.ioParam.ioPosMode = fsAtMark;
  501.  
  502.         /* how much to write? */
  503.         if (Length > WRITECELLSIZE)
  504.             {
  505.                 /* if the pending amount of data is larger than the size of a */
  506.                 /* write cell, then we only write the first writecell's worth of data */
  507.                 SerialPort->WriteCellArray[ChosenWriteCell].MyPB.ioParam.ioReqCount
  508.                     = WRITECELLSIZE;
  509.                 SerialPort->WriteCellArray[ChosenWriteCell].NumBytes = WRITECELLSIZE;
  510.                 CopyData(Data,SerialPort->WriteCellArray[ChosenWriteCell].Buffer,WRITECELLSIZE);
  511.                 PBWrite((ParamBlockRec*)&(SerialPort->WriteCellArray[ChosenWriteCell]),
  512.                     True/*async*/);
  513.                 Length -= WRITECELLSIZE;
  514.                 Data += WRITECELLSIZE;
  515.                 goto ReEntryPoint;
  516.             }
  517.          else
  518.             {
  519.                 SerialPort->WriteCellArray[ChosenWriteCell].MyPB.ioParam.ioReqCount = Length;
  520.                 SerialPort->WriteCellArray[ChosenWriteCell].NumBytes = Length;
  521.                 CopyData(Data,SerialPort->WriteCellArray[ChosenWriteCell].Buffer,Length);
  522.                 PBWrite((ParamBlockRec*)&(SerialPort->WriteCellArray[ChosenWriteCell]),
  523.                     True/*async*/);
  524.             }
  525.         if (TickCount() - StartTime > 1)
  526.             {
  527.                 APRINT(("WriteBlock took %l ticks",(long)(TickCount() - StartTime)));
  528.             }
  529.  
  530.         /* all done */
  531.         return True;
  532.     }
  533.  
  534.  
  535. /* the callback routine must NOT be profiled */
  536. #ifdef THINK_C
  537.     #if __option(profile)
  538.         #define ProfilingEnabled (True)
  539.     #else
  540.         #define ProfilingEnabled (False)
  541.     #endif
  542.  
  543.     #pragma options(!profile)
  544. #endif
  545.  
  546. #if __powerc
  547. static void        Callback(WriteCell* param)
  548. {
  549.         param->InUseFlag = 0;
  550.         param->NumBytes = 0;
  551. }
  552. #else
  553. static pascal void    Callback(void)
  554.     {
  555. #ifndef __cplusplus
  556.         register WriteCell*        HiddenParameter;
  557.  
  558.         asm{move.l A0,HiddenParameter}
  559.         HiddenParameter->InUseFlag = 0;
  560.         HiddenParameter->NumBytes = 0;
  561. #else
  562.         /* 00000000: 2F0C               MOVE.L    A4,-(A7) */
  563.         /* 00000002: 2848               MOVEA.L   A0,A4 */
  564.         /* 00000004: 426C 01D0          CLR.W     $01D0(A4) */
  565.         /* 00000008: 42AC 01D2          CLR.L     $01D2(A4) */
  566.         /* 0000000C: 285F               MOVEA.L   (A7)+,A4 */
  567.         asm(0x2f0c,0x2848,0x426c,0x01d0,0x42ac,0x01d2,0x285f);
  568. #endif
  569.     }
  570. #endif
  571.  
  572. #ifdef THINK_C
  573.     #if ProfilingEnabled
  574.         #pragma options(profile)
  575.     #endif
  576. #endif
  577.